using System;
using System.Collections.Generic;
using System.Text;
using BlogEngine.Core.Providers;

namespace BlogEngine.Core
{
	/// <summary>
	/// Represents a standard blog business entity with standard (expected) properties.
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public abstract class BlogBase<T> : BusinessBase<T, Guid> where T : BlogBase<T>, new()
	{

		/// <summary>
		/// Default constructor assigns all default values
		/// </summary>
		protected BlogBase()
			: base()
		{
			this.Id = Guid.NewGuid();
			this.DateCreated = DateTime.Now;
			this.DateModified = DateCreated;
		}

		#region properties

		private string _Title = string.Empty;
		/// <summary>
		/// Gets or sets the Title or the object.
		/// </summary>
		public string Title
		{
			get { return _Title; }
			set
			{
				if (_Title != value) MarkChanged("Title");
				_Title = value;
			}
		}

		private string _Description = string.Empty;
		/// <summary>
		/// Gets or sets the Description or the object.
		/// </summary>
		public string Description
		{
			get { return _Description; }
			set
			{
				if (_Description != value) MarkChanged("Description");
				_Description = value;
			}
		}

		private string _Slug = string.Empty;
		/// <summary>
		/// Gets or sets the Slug of the Post.
		/// A Slug is the relative URL used by the posts.
		/// </summary>
		public string Slug
		{
			get
			{
				if (string.IsNullOrEmpty(_Slug))
				{
					_Slug = Title;
					_Slug = _Slug ?? Id.ToString();
					_Slug = _Slug ?? string.Empty;
					MarkChanged("Slug");
				}
				return Utils.RemoveIllegalCharacters(_Slug);
			}
			set
			{
				if (_Slug != Utils.RemoveIllegalCharacters(value)) MarkChanged("Slug");
				{
					_Slug = Utils.RemoveIllegalCharacters(value);
				}
			}
		}

		private int _Order;
		/// <summary>
		/// Gets the order of the item.  By default this is always zero
		/// </summary>
		/// <value></value>
		public int Order
		{
			get { return _Order; }
			set
			{
				if (_Order != value) MarkChanged("Order");
				_Order = value;
			}
		}

		protected static object _SyncRoot = new object();
		protected static List<T> _Items;

		protected delegate List<T> LoadItems();
		protected static LoadItems OnLoad = LoadHelper;	// default loading mechanism

		/// <summary>
		/// Gets an unsorted list of all pages.
		/// </summary>
		public static List<T> Items
		{
			get
			{
				if (_Items == null)
				{
					lock (_SyncRoot)
					{
						if (_Items == null)
						{
							T dummy = new T(); // to force OnLoad to be set by derived class (hack)
							_Items = OnLoad();
						}
					}
				}

				return _Items;
			}
		}

		public static List<T> LoadHelper()
		{
			return BlogService.Fill<T>();
		}

		#endregion

		#region Methods

		/// <summary>
		/// Return item for the key provided.  
		/// </summary>
		/// <remarks>
		/// Uses the Items property to find the item
		/// </remarks>
		/// <param name="id"></param>
		/// <returns></returns>
		public static T GetById(string id)
		{
			Guid guid = new Guid(id);
			return GetById(guid);
		}

		/// <summary>
		/// Return item for the key provided.  
		/// </summary>
		/// <remarks>
		/// Uses the Items property to find the item
		/// </remarks>
		/// <param name="id"></param>
		/// <returns></returns>
		public static T GetById(Guid id)
		{
			return BlogBase<T>.Items.Find(delegate(T item)
			{
				return item.Id == id;
			});

		}

		/// <summary>
		/// Returns an item based on the slug provided
		/// </summary>
		/// <param name="slug"></param>
		/// <returns></returns>
		public static T GetBySlug(string slug)
		{
			return BlogBase<T>.GetBySlug(slug, DateTime.MinValue);
		}

		/// <summary>
		/// Returns an item based on the slug and a given date
		/// </summary>
		/// <param name="slug"></param>
		/// <param name="date"></param>
		/// <returns></returns>
		public static T GetBySlug(string slug, DateTime date)
		{
			return BlogBase<T>.Items.Find(delegate(T item)
			{
				//TODO: this needs to be conditional and why is it needed? - slug should be unique within T
				if (BlogSettings.Instance.TimeStampPostLinks && date != DateTime.MinValue && (item.DateCreated.Year != date.Year || item.DateCreated.Month != date.Month))
				{
					return false;
				}

				return slug.Equals(Utils.RemoveIllegalCharacters(item.Slug), StringComparison.OrdinalIgnoreCase);

			});
		}

		/// <summary>
		/// Force reload of all items
		/// </summary>
		public static void Reload()
		{
			_Items = BlogService.Fill<T>();
		}

		/// <summary>
		/// Imports item (without all standard saving events and routines)
		/// </summary>
		public void Import()
		{
			if (this.IsDeleted)
			{
				if (!this.IsNew)
				{
					BlogService.Delete<T>(((T)(object)this));
				}
			}
			else
			{
				if (this.IsNew)
				{
					BlogService.Insert<T>(((T)(object)this));
				}
				else
				{
					BlogService.Update<T>(((T)(object)this));
				}
			}
		}

		/// <summary>
		/// Reinforces the business rules by adding additional rules to the 
		/// broken rules collection.
		/// </summary>
		protected override void ValidationRules()
		{
		}

		#endregion

		#region Compare

		/// <summary>
		/// Compares order, date and title of two items and returns which is less
		/// </summary>
		/// <remarks>
		/// To support minimal trust installations we could not use reflection to check properties 
		/// </remarks>
		/// <param name="orderA"></param>
		/// <param name="stringA"></param>
		/// <param name="orderB"></param>
		/// <param name="stringB"></param>
		/// <returns></returns>
		public static int CompareOrderAndTitle(int orderA, DateTime dateA, string stringA, int orderB, DateTime dateB, string stringB)
		{
			//-ve = a < b, 0 = a = b, +ve = a > b
			//if orders match then check date, then the string
			if (orderA == orderB)
				if (DateTime.Compare(dateA, dateB) == 0)
					return string.Compare(stringA, stringB);
				else
					return DateTime.Compare(dateB, dateA);
			else
				return orderA - orderB;

		}
		#endregion
	}
}
